local map_mgr = _G.map_mgr
local Msg = require("core.Msg")
local Obj = require("core.Obj")
local Timer = require("core.Timer")
local ExcelPet = require("excel.Pet")
local SceneEvent = require("core.SceneEvent")
local Status = require("scene.Status")
local ObjPet = require("core.ObjPet")
local ObjMonster = require("core.ObjMonster")
local Skill = require("skill.Skill")
local SkillCoolDown     = require("skill.CoolDown")
local SkillCondition = require('skill.Conditions')
local PetLogic = require("pet.PetLogic")
local SkillConditions = require("skill.Conditions")
local MonsterAi = require("monster.MonsterAi")
local PetExcel = ExcelPet.pet

action = {}
condition = {}
data = data or {}
has_err =  has_err or {}

local function on_move(pet, x, y)
  if pet.bufAttr.giddy or pet.bufAttr.noMove then
    return
  end
  x = math.floor(x)
  y = math.floor(y)
  if 0 < pet.path_len and pet.path then
    if pet.tx == x and pet.ty == y then
      return
    end
  end
  pet.path = pet.path or {}
  Obj.move(pet, x, y, true)
end

local function talk(pet, text)
  local mm = Msg.gc.GC_OBJ_TALK
  mm.obj_id = pet.id
  mm.txt = text
  Obj.sendArea(mm, pet.scene_id, pet.x, pet.y)
end


local function do_ai()
  local pet = data.pet
  local ai_id = pet.last_ai_id
  local pet_ai_len = #ExcelPet.pet_ai
  if not ai_id or pet_ai_len < ai_id then
    ai_id = 0
    pet.last_ai_id = pet_ai_len
  end
  while true do
    ai_id = ai_id + 1
    if pet_ai_len < ai_id then
      ai_id = 1
    end
    local ai = ExcelPet.pet_ai[ai_id]
    if  not pet.event_time[ai_id] or
      ai.cd * 1000 <= Timer.now - pet.event_time[ai_id] then
      local fail
      for k, v in ipairs(ai.condition) do
        local fun = condition[v]
        if fun then
          if not fun(ai.condition[k + 1], ai.condition[k + 2]) then
            fail = true
            break
          end
        end
      end 
      if not fail then
        data.cur_ai = ai_id
        pet.event_time[ai_id] = Timer.now
        pet.last_ai_id = ai_id
        local str = ""
        for k, v in ipairs(ai.action) do
          local fun = action[v]
          if fun then
            if need_talk then
              str = str .. v .. "\n"
            end
            data.action_cmd = k
            send_action_has = nil
            fun(ai.action[k + 1], ai.action[k + 2])
          end
        end
        if need_talk then
          talk(pet, ai_id .. "\n" .. str)
        end
        return true
      end
    end
    if ai_id == pet.last_ai_id then
      break
    end
  end
end

function send_hide(pet)
  local mm = Msg.gc.GC_OBJ_HIDE
  mm.obj_id = pet.id
  Obj.sendArea(mm, pet.scene_id, pet.x, pet.y)
end

local function ai(pet)
  local owner = Obj.getOwner(pet)
  if not owner or (owner.db and owner.db.hp < 1) or owner.scene_id ~= pet.scene_id then
    ObjPet.destroy(pet) 
    return
  end
  if pet.db.hp < 1 then
    if pet.is_hide then
      pet.is_hide = nil
      send_hide(pet)
    end
    if pet.db.next_create_time > 0 and pet.db.next_create_time < Timer.now  then
      ObjPet.setHp(pet, 0x7fffffff)
      pet.db.next_create_time = 0
      local mm = Msg.gc.GC_REVIVE
      mm.obj_id = pet.id
      mm.hp = pet.db.hp
      mm.type = 1
      Obj.sendArea(mm, pet.scene_id, owner.x+10, owner.y+10)
      data.pet = pet
      data.owner = owner
      action.rand_talk("revive")
    end
    return
  end
  local new = owner.status == Status.HUMAN_STATUS_HIDE and 1 or nil
  if new ~= pet.is_hide then
    pet.is_hide = new
    send_hide(pet)
  end
  if pet.wake_up_time and Timer.now < pet.wake_up_time then
    return
  end
  data.pet = pet
  data.owner = owner
  data.attacker = Obj.getObj(pet.attacker_id, pet.attacker_uid)
  local attacker = data.attacker
  if pet.attacker_id and not (attacker and attacker.scene_id == pet.scene_id and 0 < Obj.getHp(attacker) and attacker.status ~= Status.HUMAN_STATUS_HIDE) then
    pet.attacker_id = nil
    pet.free_time = Timer.now
  end
  
  if pet.qianyao_begin_time then  --蓄力
    if pet.qianyao_begin_time < Timer.now then
       pet.qianyao_begin_time = nil
       if pet.status == Status.HUMAN_STATUS_ROTATION or pet.status == Status.HUMAN_STATUS_ROTATION1 or pet.status == Status.HUMAN_STATUS_GUIDE or pet.status == Status.HUMAN_STATUS_SING then
          return
       end
       Skill.use(pet, pet.qianyao_skill_id, pet.qianyao_x, pet.qianyao_y, pet.x, pet.y, attacker,pet.qianyao_skill_lv)
    end
    return
  end
  do_ai()
end

local function get_tb_can_use_skill(pet, beHit)
  if pet.skill then
      return pet.skill
  end
  local tb = {}
  PetLogic.getCanUseSkill(pet.owner.uuid, pet.db.id, tb)
  
  return tb
end

function action.del_attacker()
  data.pet.attacker_id = nil
end

function action.hp_recover(percent)
  local pet = data.pet
  ObjPet.setHp(pet, pet.db.hp + ObjPet.getHpMax(pet) * percent)
end

function action.hp_recover_use_item()
  local pet = data.pet
  ObjPet.setHp(pet, pet.db.hp + pet.db.lv * pet.pethp_lv_mul)
  if pet.pethp_use_cnt < 2 then
    pet.pethp_use_cnt = nil
    pet.pethp_lv_mul = nil
  else
    pet.pethp_use_cnt = pet.pethp_use_cnt - 1
  end
end

function action.jump_to_owner()
  local pet = data.pet
  local owner = data.owner
  Obj.jump(pet, owner.x, owner.y, Msg.gc.GC_JUMP)
  pet.attacker_id = nil
  pet.attacker_uid = nil
end

local pet_talk_hash = {}
for k, v in pairs(ExcelPet.pet_talk_ban) do
  for kk, vv in pairs(v.pet_name_list) do
    pet_talk_hash[vv] = pet_talk_hash[vv] or {}
    pet_talk_hash[vv][v.state] = pet_talk_hash[vv][v.state] or {[0] = 0}
    local t = pet_talk_hash[vv][v.state]
    t[#t + 1] = k
  end
end

function action.rand_talk(id)
  local pet_name = ExcelPet.pet[data.pet.db.id].petMingzi
  local cf = pet_talk_hash[pet_name]
  if not cf then
    return
  end
  cf = cf[id]
  if not cf then
    return
  end
  cf[0] = cf[0] + 1
  if #cf < cf[0] then
    cf[0] = 1
  end
  local mm = Msg.gc.GC_PET_TALK
  mm.txt = cf[cf[0]]
  Msg.send(mm, data.owner.fd)
end

function action.surround(len)
  local pet = data.pet
  local attacker = data.attacker
  local hudu = math.random() * 2 * math.pi
  local x = attacker.x + math.cos(hudu) * len
  local y = attacker.y + math.sin(hudu) * len
  on_move(pet, x, y)
end

function action.surround_owner(len)
  local pet = data.pet
  local owner = Obj.getObj(pet.owner.id, pet.owner.uid)
  local dx = pet.x - owner.x
  local dy = pet.y - owner.y
  local dist2 = dx * dx + dy * dy
  local x, y
  len = len or 100
  if len * len * 2 < dist2 then
    local t = len / math.sqrt(dist2)
    x = dx * t + owner.x
    y = dy * t + owner.y
  else
    local hudu = math.random() * 2 * math.pi
    x = owner.x + math.cos(hudu) * len
    y = owner.y + math.sin(hudu) * len
  end
  if pet.path_len == 1 then
    local dx = pet.path[1] - x
    local dy = pet.path[2] - y
    if dx * dx + dy * dy < 8 then
      return
    end
  end
  if pet.path_len == 0 then
    local dx = pet.x - x
    local dy = pet.y - y
    if dx * dx + dy * dy < 64 then
      return
    end
  end
  on_move(pet, x, y)
end

function action.trace(val)
  local pet = data.pet
  local attacker = data.attacker
  local xa, ya, xb, yb = pet.x, pet.y, attacker.x, attacker.y
  if 0 < attacker.path_len then
    local dx = xb - attacker.path[1]
    local dy = yb - attacker.path[2]
    if -64 < dx and dx < 64 and -64 < dy and dy < 64 then
      xb = attacker.path[1]
      yb = attacker.path[2]
    end
  end
  local dx = xa - xb
  local dy = ya - yb
  local ab = dx * dx + dy * dy
  if -1e-8 < ab and ab < 1e-8 then
    return action.surround(val)
  end
--  local x, y = (xb - xa) * (ab - val) / ab + xa, (yb - ya) * (ab - val) / ab + ya
  on_move(pet, xb, yb)
end

function action.zou_wei()
  local pet = data.pet
  local attacker = data.attacker
  local dx = pet.x - attacker.x
  local dy = pet.y - attacker.y
  local dist2 = dx * dx + dy * dy
  local minn = Timer.now + Timer.now
  local ans
  local tb = get_tb_can_use_skill(pet)
  local rr = Obj.getRadius(attacker)
  for i = 1, #tb do
    local skill_id = tb[i].skillId
    local tmp = SkillCoolDown.getSkillNextCanUseTime(pet, skill_id)
    if tmp < minn then
      minn = tmp
      ans = Skill.getAttackLen(skill_id) + rr
    end
  end
  if ans and ans * ans < dist2 then
    action.trace(150)
    return
  end
end

function action.rand_skill()
  local attacker = data.attacker
  local obj_pet = data.pet
  if not obj_pet.attacker_id then
    return
  end
  local tb = get_tb_can_use_skill(obj_pet)
  for i = #tb, 1, -1 do
    local skill_id = tb[i].skillId
    if true == SkillCondition.canUseSkill(obj_pet, skill_id) then
      MonsterAi.skill(obj_pet, skill_id, attacker.x, attacker.y, attacker, tb[i].skillLv)
--      Skill.use(obj_pet, skill_id, attacker.x, attacker.y, obj_pet.x, obj_pet.y, attacker, tb[i].skillLv)
      return
    end
  end
end

local function is_near_grid(a, b)
  local tile = 100
  local ddx = a.x - b.x
  local ddy = a.y - b.y
  if math.abs(ddx) > 110 or math.abs(ddy) >110 then
      return
  end
  local dx = math.floor(a.x / tile) - math.floor(b.x / tile)
  local dy = math.floor(a.y / tile) - math.floor(b.y / tile)
  return dx * dx + dy * dy < 3
end
function condition.can_use_skill()
  if data.owner.hide then
    return
  end
  local pet = data.pet
  local attacker = data.attacker
  local dx = pet.x - attacker.x
  local dy = pet.y - attacker.y
  local dist2 = dx * dx + dy * dy
  local tb = get_tb_can_use_skill(pet)
  local rr = Obj.getRadius(attacker)
  local near_grid = is_near_grid(pet, attacker)
  for i = 1, #tb do
    local skill_id = tb[i].skillId
    if SkillConditions.canUseSkill(pet, skill_id, attacker)  == true then
      if near_grid then
        return true
      end
      local dist = Skill.getAttackLen(skill_id) + rr
      if dist2 < dist * dist then
        return true
      end
    end
  end
end
function can_use_skill_by_id(pet, attacker, skill_id)
  local dx = pet.x - attacker.x
  local dy = pet.y - attacker.y
  local dist2 = dx * dx + dy * dy
  local rr = Obj.getRadius(attacker)
  local near_grid = is_near_grid(pet, attacker)
  if SkillConditions.canUseSkill(pet, skill_id, attacker)  == true then
    if near_grid then
      return true
    end
    local dist = Skill.getAttackLen(skill_id) + rr
    if dist2 < dist * dist then
      return true
    end
  end
end
function condition.has_target()
  return data.pet.attacker_id
end

function condition.hp(a, b)
  local pet = data.pet
  local d = pet.db.hp / ObjPet.getHpMax(pet) * 100
  return a <= d and d <= b
end

function condition.is_owner_stop()
  return data.owner.path_len < 1
end

function condition.is_pethp()
  local pet = data.pet
  return pet.pethp_lv_mul and pet.pethp_use_cnt
end

function condition.is_stop()
  return data.pet.path_len < 1
end

function condition.over_attacker_dist(len)
  local pet = data.pet
  local attacker = data.attacker
  local dx = pet.x - attacker.x
  local dy = pet.y - attacker.y
  return len * len < dx * dx + dy * dy
end

function condition.is_free(sec)
  local pet = data.pet
  return not pet.attacker_id and (not pet.free_time or pet.free_time + sec * 1000 < Timer.now)
end

function condition.over_owner_dist(len)
  local pet = data.pet
  local owner = data.owner
  local dx = pet.x - owner.x
  local dy = pet.y - owner.y
  return len * len < dx * dx + dy * dy
end

function condition.rand(sum)
  return math.random(1, 100) <= sum
end

local tb = {}
for k, v in pairs(condition) do
  tb["not_" .. k] = function(a, b) return not v(a, b) end
end
for k, v in pairs(tb) do
  condition[k] = v
end

function time()
  map_mgr.monster_clear()
  for k, v in pairs(Obj.scenes) do
    SceneEvent.setBan(k)    
    for _, pet in pairs(v) do
      if pet.obj_type == Obj.TYPE_PET and pet.db and (not has_err[pet.id] or 4444 < Timer.now - has_err[pet.id]) then
        has_err[pet.id] = Timer.now
        ai(pet)
        has_err[pet.id] = nil
      end
    end
  end
end
--starSkillRand beHit starSkill
--主人攻击
function onOwnerAttack(recv, sender)
  if not sender or sender == recv then
    return
  end
  local obj_pet = Obj.getObj(recv.fight_pet_id, recv.fight_pet_uid)  
  if obj_pet and 0 < obj_pet.db.hp and (not sender.monster_id or ObjMonster.getValueByMonsterType(sender, "can_select") == 1) then
    obj_pet.attacker_id = sender.id
    obj_pet.attacker_uid = sender.uid
    if 0 == PetExcel[obj_pet.db.id].beHit and math.random(1, 100) <= PetExcel[obj_pet.db.id].starSkillRand then
      if obj_pet.db.star > 0 then
        local skillId = PetExcel[obj_pet.db.id].starSkill[obj_pet.db.star]
        if true == can_use_skill_by_id(obj_pet, sender, skillId) then
          MonsterAi.skill(obj_pet, skillId, sender.x, sender.y, sender, 1)        
          return
        end
      end
    end
    local tb = get_tb_can_use_skill(obj_pet, 0)    
    for i = #tb, 1, -1 do
      local skill_id = tb[i].skillId      
      if true == can_use_skill_by_id(obj_pet, sender, skill_id) then --Skill.canUseSkill(obj_pet, skill_id) then        
--        Skill.use(obj_pet, skill_id, sender.x, sender.y, obj_pet.x, obj_pet.y, sender, tb[i].skillLv)
        MonsterAi.skill(obj_pet, skill_id, sender.x, sender.y, sender, tb[i].skillLv)
        return
      end
    end
  end
end
--主人被攻击
function onOwnerBeHit(attacker, human)
  if not attacker or attacker == human then
    return
  end
  
  local obj_pet = Obj.getObj(human.fight_pet_id, human.fight_pet_uid)
  if obj_pet and 0 < obj_pet.db.hp and (not attacker.monster_id or ObjMonster.getValueByMonsterType(attacker, "can_select") == 1) then
    if 1 == PetExcel[obj_pet.db.id].beHit and math.random(1, 100) <= PetExcel[obj_pet.db.id].starSkillRand then
      if obj_pet.db.star > 0 then
        local skillId = PetExcel[obj_pet.db.id].starSkill[obj_pet.db.star]
        if true == can_use_skill_by_id(obj_pet, attacker, skillId) then
          MonsterAi.skill(obj_pet, skillId, attacker.x, attacker.y, attacker, 1)        
          return
        end
      end
    end
    if not obj_pet.attacker_id then
      obj_pet.attacker_id = attacker.id
      obj_pet.attacker_uid = attacker.uid    
      local tb = get_tb_can_use_skill(obj_pet)
      for i = #tb, 1, -1 do
        local skill_id = tb[i].skillId
        if true == can_use_skill_by_id(obj_pet, attacker, skill_id) then
          MonsterAi.skill(obj_pet, skill_id, attacker.x, attacker.y, attacker, tb[i].skillLv)        
          return
        end
      end
    end
    
    
  end
end